/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flopbox.server;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteObject;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author lasse
 */
public class FlopBoxServer {

    public static String HOST = "158.38.43.32";
    public static String LOCAL_HOST = "localhost";
    public static String NAME = "flopBoxServer";
    public static int PORT = 1337;
    // Add another place later
    public static String FLOPBOX_PATH = System.getProperty("user.home") + File.separator + "flopBox" + File.separator;
    
    public static DatabaseConnector db;

    public static class FileServer extends UnicastRemoteObject implements RemoteFlopboxServer {
        // List of allowed transfers
        
        public FileServer() throws RemoteException {
        }
        private static HashMap<Integer, String> uploadReady = new HashMap<>();
        private static HashMap<Integer, String> downloadReady = new HashMap<>();
        private static HashMap<Integer, Date> fileDates = new HashMap<>();
        private static HashMap<Integer, Integer> allCounters = new HashMap<>();
        
        private static int currFileId = 1;

        @Override
        public void sendString(String s) throws RemoteException {
            System.out.println("Insert successful?");
            try { 
                 db.insertFileInfo(new FileInfo(new Date(), s));
            } catch (Exception e){
                e.printStackTrace();
            }
                   
        }

        @Override
        public int initUpload(FileInfo f) throws RemoteException {
            // Check if file exists on the server
            
            
            
            // If file is old or doesn't exist
            uploadReady.put(++currFileId, f.getFilename());
            fileDates.put(currFileId, f.getModified());
            return currFileId;
        }
        
        
        @Override
        public void doneUploadingFile(int id) throws RemoteException {
            
            db.insertFileInfo(new FileInfo(fileDates.get(id), uploadReady.get(id)));
            uploadReady.remove(id);
            printFileList();
            
        }
        private void printFileList(){
            List<FileInfo> fileList=this.getFilesModified(new ArrayList<FileInfo>());
            for(FileInfo fi:fileList){
                System.out.println("Filename "+fi.getFilename()+":LastModified "+fi.getModified()+":isDeleted "+fi.isDeleted());
            }
        }
        

        @Override
        public void uploadFile(int id, byte[] ar) throws RemoteException {
            String res = "";
            for (int i = 0; i < ar.length; i++) {
                res += ar[i];
            }

            if (uploadReady.containsKey(id)) {
                String filename = uploadReady.get(id);

                File f = new File(FLOPBOX_PATH + "../" + File.separator + filename);
                FileOutputStream fos = null;
                FileChannel channel = null;
                FileLock lock = null;
                do {
                    try {
                        fos = new FileOutputStream(f, true);
                        channel = fos.getChannel();
                        lock = channel.tryLock();
                        if (lock != null) {
                            DataOutputStream dis = new DataOutputStream(fos);
                            try {
                                dis.write(ar);
                                dis.flush();
                            } catch (IOException ex) {
                                Logger.getLogger(FlopBoxServer.class.getName()).log(Level.SEVERE, null, ex);
                            } finally {
                                lock.release();
                            }
                        }
                    } catch (Exception e) {
                    } finally {
                        if (fos != null) {
                            try {
                                fos.close();
                            } catch (IOException ex) {
                                Logger.getLogger(FlopBoxServer.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }

                        if (channel != null) {
                            try {
                                channel.close();
                            } catch (IOException ex) {
                                Logger.getLogger(FlopBoxServer.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }

                        if (lock != null && lock.isValid()) {
                            try {
                                lock.release();
                            } catch (IOException ex) {
                                Logger.getLogger(FlopBoxServer.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                } while (fos == null);
            }
        }

        private boolean isFileReady(File f) {
            int sleepTime = 1000;
            FileLock lock;
            while (!canOpenFile(f)) {
                try {
                    Thread.sleep(sleepTime);
                    sleepTime *= 2;
                } catch (InterruptedException ex) {
                    Logger.getLogger(FlopBoxServer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return true;
        }

        private boolean canOpenFile(File f) {
            boolean canOpen = false;
            try (FileInputStream fin = new FileInputStream(f)) {
                canOpen = true;
            } catch (Exception e) {
                canOpen = false;
            }
            return canOpen;
        }

        // TODO
        @Override
        public ArrayList<FileInfo> getFilesModified(ArrayList<FileInfo> metaFiles) {
            try {
                //Construct a list of FileInfo objects from the server.
                ArrayList<FileInfo> list=db.getAllFiles();
                System.out.println("List size:"+list.size());
                java.util.Collections.sort(list);
                System.out.println("List size2"+list.size());
                for(FileInfo fi:metaFiles){
                    int index=java.util.Collections.binarySearch(list, fi);
                    System.out.println("List size3"+list.size());
                    System.out.println("index:"+index);
                    //If the server has one of the files listed from the client,       
                    if(index>=0){
                        FileInfo fic=list.get(index);
                        //the server will remove FileInfo fomr the returned list
                        //if modified date is equal
                        if(fic.getModified().equals(fi.getModified())){
                           list.remove(index); 
                        }
                    }
                    //If the FileInfo does not exist on server, it will be added to 
                    //the returned list
                    else{
                        list.add(fi);
                    }
                }
                return list;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public int initDownload(FileInfo f) throws RemoteException {
            
            return 0;
        }

        @Override
        public byte[] downloadFile(int id) throws RemoteException {
            
            return null;
        }

        
    }
    public static void main(String[] args) throws Exception {

        RemoteFlopboxServer server = new FileServer();
        Registry registry = LocateRegistry.createRegistry(PORT);
        registry.rebind(NAME, server);

        System.out.println("Server ready");
        
        db = new DatabaseConnector();

    }
}
